from collections import defaultdict
from typing import List, Mapping, Type
from .base import BlockT
from .all_blocks import *


T = Type[BlockT]


def get_inverse_transitive_closure(deps: Mapping[T, List[T]], max_iter: int = 1000) -> Mapping[T, List[T]]:
    """Find a transifive closure of an inverse of the given mapping.
    """
    result = defaultdict(set)
    for block, block_deps in deps.items():
        for d in block_deps:
            result[d].add(block)
    total_size = 0
    for _ in range(max_iter):
        cur_size = 0
        for a, a_block_effects in result.items():
            for b in list(a_block_effects):
                for c in result[b]:
                    a_block_effects.add(c)
            cur_size += len(a_block_effects)
        if cur_size == total_size:
            break
        total_size = cur_size
    else:
        raise RuntimeError(
            'Maximum number of iterations exceeded, something is wrong with block dependencies ' \
            '(perhaps there is a cycle)'
        )
    return {
        k: list(v)
        for k, v in result.items()
    }


BLOCK_DEPENDENCIES: Mapping[T, List[T]] = {
    IdeaBlock: [],
    SourcesBlock: [IdeaBlock],
    TopicsBlock: [IdeaBlock, SourcesBlock],
    ReviewBlock: [SourcesBlock, TopicsBlock],
    TitleBlock: [IdeaBlock, TopicsBlock],
    AbstractBlock: [IdeaBlock, TitleBlock, TopicsBlock],
    MethodBlock: [IdeaBlock, TitleBlock, TopicsBlock, ReviewBlock],
}
"""Mapping from a block class to a list of blocks that should be evaluated before the block.
"""

BLOCK_EFFECTS: Mapping[T, List[T]] = get_inverse_transitive_closure(BLOCK_DEPENDENCIES)
"""Mapping from a block class to a list of block classes affected by the block.
"""
